package com.doublefish.doublefishrobot.utils.ble;


import com.doublefish.doublefishrobot.utils.ble.bean.CRCUtils;

import java.io.UnsupportedEncodingException;

/**
 * Created by peak on 2023/11/1
 */
public class DFS8ProtocolMaker {

    private byte[] machineID;   //机器id  2字节 通用id为0x0000  目前测试的机器为 0x0001

    public DFS8ProtocolMaker() {
        //初始化方法
        machineID = new byte[]{0x00,0x00};
    }

    public void setMachineID(byte byte0, byte byte1) {
        machineID[0] = byte0;
        machineID[1] = byte1;
    }

    //获取机器id
    public byte[] getMachineId() {
        byte[] checkData = new byte[7];
        checkData[0] = (byte)0xAF;
        checkData[1] = (byte)0x00;
        checkData[2] = (byte)0x04;
        checkData[3] = (byte)0x00;  //用通用id
        checkData[4] = (byte)0x00;
        checkData[5] = (byte)0x0F;

        //计算 校验位
        byte check = CRCUtils.calcCrc8(checkData);
        checkData[6] = check;

        return checkData;
    }

    //连接机器
    public byte[] connectMachine() {
        byte[] checkData = new byte[7];
        checkData[0] = (byte)0xAF;
        checkData[1] = (byte)0x00;
        checkData[2] = (byte)0x04;
        //用通用id连接
        checkData[3] = (byte)0x00;
        checkData[4] = (byte)0x00;
        checkData[5] = (byte)0x0A;

        //计算 校验位
        byte check = CRCUtils.calcCrc8(checkData);
        checkData[6] = check;

        return checkData;
    }

    //断开机器
    public byte[] disconnectMachine() {
        byte[] checkData = new byte[7];
        checkData[0] = (byte)0xAF;
        checkData[1] = (byte)0x00;
        checkData[2] = (byte)0x04;
        checkData[3] = machineID[0];
        checkData[4] = machineID[1];
        checkData[5] = (byte)0x17;

        //计算 校验位
        byte check = CRCUtils.calcCrc8(checkData);
        checkData[6] = check;

        return checkData;
    }

    //心跳
    public byte[] heartBeat() {
        byte[] checkData = new byte[7];
        checkData[0] = (byte)0xAF;
        checkData[1] = (byte)0x00;
        checkData[2] = (byte)0x04;
        checkData[3] = machineID[0];
        checkData[4] = machineID[1];
        checkData[5] = (byte)0x09;

        //计算 校验位
        byte check = CRCUtils.calcCrc8(checkData);
        checkData[6] = check;

        return checkData;
    }

    //测试发球  单球测试
    public byte[] testBall(String script) {
        //将脚本进行分割， 分割成多条独立的发球将脚本
        String[] steps = splitScript(script);
        //这里只做单球测试  取第一条脚本
        if (steps == null || steps.length == 0) {
            return null;
        }
        String step = steps[0];
        if (step.length() == 0) {
            return null;
        }
        //将脚本继续分解，提取出各个发球参数项
        String[] params = parseStepScript(step);
        if (params == null || params.length < 7) {
            //发球参数项为7个 可能有第8个为空
            return null;
        }

        //固定字节数  数据头1+包长度2+机器ID2+命令位1+左右手标志1+...+校验位1 = 8
        int length= 8;
        //计算参数所需要的字节数
        int paramLength = calculateParamsLengthForSingleBall(params);
        //总长度
        length += paramLength;

        byte[] checkData = new byte[length];
        checkData[0] = (byte)0xAF;
        checkData[1] = (byte)((length - 3) >> 8);
        checkData[2] = (byte)((length - 3) & 0x00FF);
        checkData[3] = machineID[0];
        checkData[4] = machineID[1];
        checkData[5] = (byte)0x13;        //测试发球的指令
        checkData[6] = 0x01;              //左手0x00 右手0x01 先写死0x01

        int index = 7;
        //循环取参数    前面已经判断过size>=7 可能存在第8个参数为""，忽略掉
        //单球测试不需要前面2个参数
        for (int i = 2; i < 7; i++) {
            String[] arr = analyseParam(params[i]);
            if (arr == null || arr.length == 0) {
                checkData[index] = 0x0;
                index++;
                checkData[index] = (byte) (i == 6 ? 90 : 25);
                index++;
                continue;
            }
            for (int j = 0; j < arr.length; j++) {
                checkData[index] = (byte) stringToInt(arr[j]);
                index++;
            }
        }
        //计算 校验位
        byte check = CRCUtils.calcCrc8(checkData);
        checkData[length - 1] = check;

        return checkData;
    }

    //读目录下文件名
    public byte[] readFileName(int dir) {
        byte[] checkData = new byte[8];
        checkData[0] = (byte)0xAF;
        checkData[1] = (byte)0x00;
        checkData[2] = (byte)0x05;
        checkData[3] = machineID[0];
        checkData[4] = machineID[1];
        checkData[5] = (byte)0x07;

        checkData[6] = (byte)dir;

        //计算 校验位
        byte check = CRCUtils.calcCrc8(checkData);
        checkData[7] = check;

        return checkData;
    }

    //读文件
    public byte[] readFile(int dir, String fileName) {
        if (fileName == null || fileName.length() == 0) {
            return null;
        }
        byte[] fileNameData =  stringToBytes(fileName);
        if (fileNameData == null) {
            return null;
        }
        int length = 9 + (int)fileNameData.length;

        byte[] checkData = new byte[length];
        checkData[0] = (byte)0xAF;
        checkData[1] = (byte)((length - 3) >> 8);
        checkData[2] = (byte)((length - 3) & 0x00FF);
        checkData[3] = machineID[0];
        checkData[4] = machineID[1];
        checkData[5] = (byte)0x06;    //指令 06
        checkData[6] = (byte)dir;    //目录
        checkData[7] = (byte)fileNameData.length;    //文件名长度
        //文件名
        for (int i = 0; i < fileNameData.length; i++) {
            checkData[8 + i] = fileNameData[i];
        }
        //计算 校验位
        byte check = CRCUtils.calcCrc8(checkData);
        checkData[length - 1] = check;

        return checkData;
    }

    //停止发球脚本
    public byte[] stopScript() {
        byte[] checkData = new byte[7];
        checkData[0] = (byte)0xAF;
        checkData[1] = (byte)0x00;
        checkData[2] = (byte)0x04;
        checkData[3] = machineID[0];
        checkData[4] = machineID[1];
        checkData[5] = (byte)0x0C;

        //计算 校验位
        byte check = CRCUtils.calcCrc8(checkData);
        checkData[6] = check;

        return checkData;
    }

    //暂停发球脚本
    public byte[] pauseScript() {
        byte[] checkData = new byte[7];
        checkData[0] = (byte)0xAF;
        checkData[1] = (byte)0x00;
        checkData[2] = (byte)0x04;
        checkData[3] = machineID[0];
        checkData[4] = machineID[1];
        checkData[5] = (byte)0x0D;

        //计算 校验位
        byte check = CRCUtils.calcCrc8(checkData);
        checkData[6] = check;

        return checkData;
    }

    //恢复发球脚本
    public byte[] resumeScript() {
        byte[] checkData = new byte[7];
        checkData[0] = (byte)0xAF;
        checkData[1] = (byte)0x00;
        checkData[2] = (byte)0x04;
        checkData[3] = machineID[0];
        checkData[4] = machineID[1];
        checkData[5] = (byte)0x0E;

        //计算 校验位
        byte check = CRCUtils.calcCrc8(checkData);
        checkData[6] = check;

        return checkData;
    }

    /**
     启动脚本
     @param dir  目录
     @param fileName 脚本名
     @param loopInterval 循环间隔 ms 默认1000
     @param isTime   时长还是次数
     @param time     时长
     @param count    次数
     */
    public byte[] runScript(int dir, String fileName, int loopInterval, boolean isTime, int time, int count) {
        //先假设fileName = 01R02正手热身训练
        //   fileName = "01R02";
        if (fileName == null || fileName.length() == 0) {
            return null;
        }
        byte[] fileNameData = stringToBytes(fileName);
        if (fileNameData == null) {
            return null;
        }
        int timeOrCountSize = isTime ? 4 : 2;
        int length = 14 + timeOrCountSize + (int)fileNameData.length;

        byte[] checkData = new byte[length];
        checkData[0] = (byte)0xAF;
        checkData[1] = (byte)((length - 3) >> 8);
        checkData[2] = (byte)((length - 3) & 0x00FF);
        checkData[3] = machineID[0];
        checkData[4] = machineID[1];
        checkData[5] = (byte)0x0B;    //指令 0B

        checkData[6] = (byte)0x01;    //左右手 左手0x00 右手0x01 先写死0x01
        checkData[7] = (byte)(loopInterval >> 16);    //循环间隔3个字节 （间隔时间从500ms到999000ms 默认1000ms
        checkData[8] = (byte)((loopInterval >> 8) & 0xff);
        checkData[9] = (byte)(loopInterval & 0xFF);
        checkData[10] = isTime ? (byte)0x01 : (byte)0x00 ;   //结束参数 0x00表示结束标志是次数 0x01表示结束表示是时间

        if (isTime) {
            //按时间发球  脚本循环的时间，用四个十六进制表示
            checkData[11] = (byte)(time >> 24);
            checkData[12] = (byte)((time >> 16) & 0xFF);
            checkData[13] = (byte)((time >> 8) & 0xFF);
            checkData[14] = (byte)(time & 0xFF);
        } else {
            //按次数发球  //脚本循环次数，用两个十六进制数表示
            checkData[11] = (byte)(count >> 8);
            checkData[12] = (byte)(count & 0xFF);
        }
        checkData[11 + timeOrCountSize] = (byte)dir;    //目录
        checkData[12 + timeOrCountSize] = (byte)fileNameData.length;    //文件名长度
        //文件名
        for (int i = 0; i < fileNameData.length; i++) {
            checkData[13 + timeOrCountSize + i] = fileNameData[i];
        }
        //计算 校验位
        byte check = CRCUtils.calcCrc8(checkData);
        checkData[length - 1] = check;

        return checkData;
    }

    /**
     写入脚本 （头部）
     由于发球机缓冲区 只有256byte 所以对于脚本可能需要分包
     @param dir  目录
     @param fileName 脚本名
     @param script   脚本内容
     */
    public byte[] writeScript(int dir, String fileName, String script) {
        if (fileName == null || fileName.length() == 0) {
            return null;
        }
        if (script == null || script.length() == 0) {
            return null;
        }

        byte[] fileNameData = stringToBytes(fileName);
        if (fileNameData == null) {
            return null;
        }
        byte[] scriptData = stringToBytes(script);
        if (scriptData == null) {
            return null;
        }

        int length = 11 + (int)fileNameData.length + (int)scriptData.length;

        byte[] checkData = new byte[length];
        checkData[0] = (byte)0xAF;
        checkData[1] = (byte)((length - 3) >> 8);
        checkData[2] = (byte)((length - 3) & 0x00FF);
        checkData[3] = machineID[0];
        checkData[4] = machineID[1];
        checkData[5] = (byte)0x01;    //指令 01  写文件
        checkData[6] = (byte)dir;                  //目录
        checkData[7] = (byte)fileNameData.length;       //文件名长度
        //文件名
        int i = 0;
        for (i = 0; i < fileNameData.length; i++) {
            checkData[8 + i] = fileNameData[i];
        }
        checkData[8 + i] = (byte)(scriptData.length >> 8);       //数据长度 2字节
        checkData[9 + i] = (byte) (scriptData.length & 0x00FF);  //数据长度
        //数据内容
        for (int j = 0; j < scriptData.length; j++) {
            checkData[10 + i + j] = scriptData[j];
        }
        //计算 校验位
        byte check = CRCUtils.calcCrc8(checkData);
        checkData[length - 1] = check;

        return checkData;
    }

    /**
     写入脚本尾部 （分包后 除头部，其他都是尾部）
     由于发球机缓冲区 只有256byte 所以对于脚本可能需要分包
     @param dir  目录
     @param fileName 脚本名
     @param script   脚本内容
     */
    public byte[] writeTrailScript(int dir, String fileName, String script) {
        if (fileName == null || fileName.length() == 0) {
            return null;
        }
        if (script == null || script.length() == 0) {
            return null;
        }

        byte[] fileNameData = stringToBytes(fileName);
        if (fileNameData == null) {
            return null;
        }
        byte[] scriptData = stringToBytes(script);
        if (scriptData == null) {
            return null;
        }

        int length = 11 + (int)fileNameData.length + (int)scriptData.length;

        byte[] checkData = new byte[length];
        checkData[0] = (byte)0xAF;
        checkData[1] = (byte)((length - 3) >> 8);
        checkData[2] = (byte)((length - 3) & 0x00FF);
        checkData[3] = machineID[0];
        checkData[4] = machineID[1];
        checkData[5] = (byte)0x02;    //指令 02  写文件到尾部，这个数据只有指令不同， 其他与上面的一模一样
        checkData[6] = (byte)dir;                  //目录
        checkData[7] = (byte)fileNameData.length;       //文件名长度
        //文件名
        int i = 0;
        for (i = 0; i < fileNameData.length; i++) {
            checkData[8 + i] = fileNameData[i];
        }
        checkData[8 + i] = (byte)(scriptData.length >> 8);       //数据长度 2字节
        checkData[9 + i] = (byte) (scriptData.length & 0x00FF);  //数据长度
        //数据内容
        for (int j = 0; j < scriptData.length; j++) {
            checkData[10 + i + j] = scriptData[j];
        }
        //计算 校验位
        byte check = CRCUtils.calcCrc8(checkData);
        checkData[length - 1] = check;

        return checkData;
    }

    /**
     写入脚本完毕 文件数据已全部发送结束
     @param dir  目录
     @param fileName 脚本名
     */
    public byte[] finishWritScript(int dir, String fileName) {
        if (fileName == null || fileName.length() == 0) {
            return null;
        }
        byte[] fileNameData =  stringToBytes(fileName);
        if (fileNameData == null) {
            return null;
        }
        int length = 9 + (int)fileNameData.length;

        byte[] checkData = new byte[length];
        checkData[0] = (byte)0xAF;
        checkData[1] = (byte)((length - 3) >> 8);
        checkData[2] = (byte)((length - 3) & 0x00FF);
        checkData[3] = machineID[0];
        checkData[4] = machineID[1];
        checkData[5] = (byte)0x03;    //指令 03
        checkData[6] = (byte)dir;    //目录
        checkData[7] = (byte)fileNameData.length;    //文件名长度
        //文件名
        for (int i = 0; i < fileNameData.length; i++) {
            checkData[8 + i] = fileNameData[i];
        }
        //计算 校验位
        byte check = CRCUtils.calcCrc8(checkData);
        checkData[length - 1] = check;

        return checkData;
    }

    /**
     删除脚本
     @param dir  目录
     @param fileName 脚本名
     */
    public byte[] deleteScript(int dir, String fileName) {
        if (fileName == null || fileName.length() == 0) {
            return null;
        }
        byte[] fileNameData =  stringToBytes(fileName);
        if (fileNameData == null) {
            return null;
        }
        int length = 9 + (int)fileNameData.length;

        byte[] checkData = new byte[length];
        checkData[0] = (byte)0xAF;
        checkData[1] = (byte)((length - 3) >> 8);
        checkData[2] = (byte)((length - 3) & 0x00FF);
        checkData[3] = machineID[0];
        checkData[4] = machineID[1];
        checkData[5] = (byte)0x04;    //指令 04
        checkData[6] = (byte)dir;    //目录
        checkData[7] = (byte)fileNameData.length;    //文件名长度
        //文件名
        for (int i = 0; i < fileNameData.length; i++) {
            checkData[8 + i] = fileNameData[i];
        }
        //计算 校验位
        byte check = CRCUtils.calcCrc8(checkData);
        checkData[length - 1] = check;

        return checkData;
    }

    //获取当前脚本的状态
    public byte[] getScriptState() {
        byte[] checkData = new byte[7];
        checkData[0] = (byte)0xAF;
        checkData[1] = (byte)0x00;
        checkData[2] = (byte)0x04;
        checkData[3] = machineID[0];
        checkData[4] = machineID[1];
        checkData[5] = (byte)0x19;

        //计算 校验位
        byte check = CRCUtils.calcCrc8(checkData);
        checkData[6] = check;

        return checkData;
    }

    //获取机器的状态
    public byte[] getMachineState() {
        byte[] checkData = new byte[7];
        checkData[0] = (byte)0xAF;
        checkData[1] = (byte)0x00;
        checkData[2] = (byte)0x04;
        checkData[3] = machineID[0];
        checkData[4] = machineID[1];
        checkData[5] = (byte)0x08;

        //计算 校验位
        byte check = CRCUtils.calcCrc8(checkData);
        checkData[6] = check;

        return checkData;
    }

    //获取机械偏移
    public byte[] getMachineOffset() {
        byte[] checkData = new byte[7];
        checkData[0] = (byte)0xAF;
        checkData[1] = (byte)0x00;
        checkData[2] = (byte)0x04;
        checkData[3] = machineID[0];
        checkData[4] = machineID[1];
        checkData[5] = (byte)0x10;

        //计算 校验位
        byte check = CRCUtils.calcCrc8(checkData);
        checkData[6] = check;

        return checkData;
    }

    /**
     * //设置机械偏移
     *
     * @param landingPoint 水平方向 左右
     * @param elevation    垂直方向  仰角
     * @param sideSpin     侧旋
     * @return 返回的NSData
     */
    public byte[] setMachineOffset(int landingPoint, int elevation, int sideSpin) {
        byte[] checkData = new byte[13];
        checkData[0] = (byte)0xAF;
        checkData[1] = (byte)0x00;
        checkData[2] = (byte)0x0A;
        checkData[3] = machineID[0];
        checkData[4] = machineID[1];
        checkData[5] = (byte)0x11;
        checkData[6] = (byte)(landingPoint >> 8);      //水平方向  位移 取第一个字节
        checkData[7] = (byte)(landingPoint & 0x00FF);  //水平方向  按位与 取第二个字节;
        checkData[8] = (byte)(elevation >> 8);      //垂直方向  位移 取第一个字节
        checkData[9] = (byte)(elevation & 0x00FF);  //垂直方向  按位与 取第二个字节;
        checkData[10] = (byte)(sideSpin >> 8);      //侧旋方向  位移 取第一个字节
        checkData[11] = (byte)(sideSpin & 0x00FF);  //侧旋方向  按位与 取第二个字节;

        //计算 校验位
        byte check = CRCUtils.calcCrc8(checkData);
        checkData[12] = check;

        return checkData;
    }

    //保存机械偏移（将当前设置的偏移值,保存到内存卡）
    public byte[] saveMachineOffset() {
        byte[] checkData = new byte[7];
        checkData[0] = (byte)0xAF;
        checkData[1] = (byte)0x00;
        checkData[2] = (byte)0x04;
        checkData[3] = machineID[0];
        checkData[4] = machineID[1];
        checkData[5] = (byte)0x12;

        //计算 校验位
        byte check = CRCUtils.calcCrc8(checkData);
        checkData[6] = check;

        return checkData;
    }

    //获取固件版本
    public byte[] getFirmware() {
        byte[] checkData = new byte[7];
        checkData[0] = (byte)0xAF;
        checkData[1] = (byte)0x00;
        checkData[2] = (byte)0x04;
        checkData[3] = machineID[0];
        checkData[4] = machineID[1];
        checkData[5] = (byte)0x14;

        //计算 校验位
        byte check = CRCUtils.calcCrc8(checkData);
        checkData[6] = check;

        return checkData;
    }

    /**
     写入文件 （头部）
     由于发球机缓冲区 只有256byte 所以对于脚本可能需要分包
     @param dir  目录
     @param fileName 文件名
     @param fileData  文件内容
     */
    public byte[] writeFile(int dir, String fileName, byte[] fileData) {
        if (fileName == null || fileName.length() == 0) {
            return null;
        }
        if (fileData == null || fileData.length == 0) {
            return null;
        }

        byte[] fileNameData = stringToBytes(fileName);
        if (fileNameData == null) {
            return null;
        }

        int length = 11 + (int)fileNameData.length + (int)fileData.length;

        byte[] checkData = new byte[length];
        checkData[0] = (byte)0xAF;
        checkData[1] = (byte)((length - 3) >> 8);
        checkData[2] = (byte)((length - 3) & 0x00FF);
        checkData[3] = machineID[0];
        checkData[4] = machineID[1];
        checkData[5] = (byte)0x01;    //指令 01  写文件
        checkData[6] = (byte)dir;                  //目录
        checkData[7] = (byte)fileNameData.length;       //文件名长度
        //文件名
        int i = 0;
        for (i = 0; i < fileNameData.length; i++) {
            checkData[8 + i] = fileNameData[i];
        }
        checkData[8 + i] = (byte)(fileData.length >> 8);       //数据长度 2字节
        checkData[9 + i] = (byte) (fileData.length & 0x00FF);  //数据长度
        //数据内容
        for (int j = 0; j < fileData.length; j++) {
            checkData[10 + i + j] = fileData[j];
        }
        //计算 校验位
        byte check = CRCUtils.calcCrc8(checkData);
        checkData[length - 1] = check;

        return checkData;
    }

    /**
     写入脚本尾部 （分包后 除头部，其他都是尾部）
     由于发球机缓冲区 只有256byte 所以对于脚本可能需要分包
     @param dir  目录
     @param fileName 文件名
     @param fileData  文件内容
     */
    public byte[] writeFileTrail(int dir, String fileName, byte[] fileData) {
        if (fileName == null || fileName.length() == 0) {
            return null;
        }
        if (fileData == null || fileData.length == 0) {
            return null;
        }

        byte[] fileNameData = stringToBytes(fileName);
        if (fileNameData == null) {
            return null;
        }

        int length = 11 + (int)fileNameData.length + (int)fileData.length;

        byte[] checkData = new byte[length];
        checkData[0] = (byte)0xAF;
        checkData[1] = (byte)((length - 3) >> 8);
        checkData[2] = (byte)((length - 3) & 0x00FF);
        checkData[3] = machineID[0];
        checkData[4] = machineID[1];
        checkData[5] = (byte)0x02;    //指令 02  写文件到尾部，这个数据只有指令不同， 其他与上面的一模一样
        checkData[6] = (byte)dir;                  //目录
        checkData[7] = (byte)fileNameData.length;       //文件名长度
        //文件名
        int i = 0;
        for (i = 0; i < fileNameData.length; i++) {
            checkData[8 + i] = fileNameData[i];
        }
        checkData[8 + i] = (byte)(fileData.length >> 8);       //数据长度 2字节
        checkData[9 + i] = (byte) (fileData.length & 0x00FF);  //数据长度
        //数据内容
        for (int j = 0; j < fileData.length; j++) {
            checkData[10 + i + j] = fileData[j];
        }
        //计算 校验位
        byte check = CRCUtils.calcCrc8(checkData);
        checkData[length - 1] = check;

        return checkData;
    }

    /**
     写入脚本完毕 文件数据已全部发送结束
     @param dir  目录
     @param fileName 文件名
     */
    public byte[] finishWriteFile(int dir, String fileName) {
        if (fileName == null || fileName.length() == 0) {
            return null;
        }
        byte[] fileNameData =  stringToBytes(fileName);
        if (fileNameData == null) {
            return null;
        }
        int length = 9 + (int)fileNameData.length;

        byte[] checkData = new byte[length];
        checkData[0] = (byte)0xAF;
        checkData[1] = (byte)((length - 3) >> 8);
        checkData[2] = (byte)((length - 3) & 0x00FF);
        checkData[3] = machineID[0];
        checkData[4] = machineID[1];
        checkData[5] = (byte)0x03;    //指令 03
        checkData[6] = (byte)dir;    //目录
        checkData[7] = (byte)fileNameData.length;    //文件名长度
        //文件名
        for (int i = 0; i < fileNameData.length; i++) {
            checkData[8 + i] = fileNameData[i];
        }
        //计算 校验位
        byte check = CRCUtils.calcCrc8(checkData);
        checkData[length - 1] = check;

        return checkData;
    }

    //region encode
    private byte[] stringToBytes(String str) {
        try {
            return str.getBytes("GB18030");
        }catch (UnsupportedEncodingException exception) {
            exception.printStackTrace();
            return null;
        }
    }

    private String bytesToString(byte[] bytes) {
        try {
            return new String(bytes, "GB18030");
        } catch (UnsupportedEncodingException exception) {
            exception.printStackTrace();
            return null;
        }
    }

    //endregion


    //region 脚本处理
    //将脚本进行分割， 分割成多条独立的发球将脚本
    private String[] splitScript(String script) {
        if (script == null || script.length() == 0) {
            return null;
        }
        return script.split(",#\n");
    }

    //将脚本继续分解，提取出各个发球参数项
    private String[] parseStepScript(String step) {
        if (step == null || step.length() == 0) {
            return null;
        }
        return step.split(",#");
    }

    //计算单个发球的参数的 长度 （仅适用于单球测试）
    private int calculateParamsLengthForSingleBall(String[] params) {
        int length = 0;
        //单球测试不需要前面2个参数
        for (int i = 2; i < 7; i++) {
            String[] arr = analyseParam(params[i]);
            if (arr== null || arr.length == 0) {
                length +=2;
                continue;
            }
            int size = stringToInt(arr[0]);
            if (size == 0) {
                length += 2;
            } else {
                length += size * 2 + 1;
            }
        }
        return length;
    }

    //解析单个参数
    private String[] analyseParam(String param) {
        if (param == null || param.length() == 0) {
            return null;
        }
        //字符串中有 ，和 -   先把 ，替换成 -
        String str = param.replace("," ,"-");
        return str.split("-");
    }

    private int stringToInt(String str) {
        int num = 0;
        try {
            num = Integer.parseInt(str);
        } catch (NumberFormatException e) {
            e.printStackTrace();
        }
        return num;
    }

    //endregion

}
